<?php

mysqli_report(MYSQLI_REPORT_ERROR);

class Soul_Db_Driver_Mysqli extends Soul_Db_Driver_Base {

    private $_link = NULL;
    private $_isTransaction = FALSE;

    public function __construct($server = NULL, $database = NULL, $username = NULL, $password = NULL){
		parent::__construct($server, $database, $username, $password);
    }
    
    public function __destruct(){
    	$this->_link = NULL;
    	$this->_isTransaction = FALSE;
    }

    public function isConnected() {
        return $this->_link ? TRUE : FALSE;
    }

    public function isTransaction() {
        return $this->_isTransaction;
    }

    public function escapeString(&$value) {
    	if(is_array($value)){
    		foreach($value as &$v){
    			$v = $this->_link->escape_string($v);
    		}
    		return $value;
    	}
        return $this->_link->escape_string($value);
    }

    public function setSql($sql, $params = array()) {
        $this->originSql = $sql;
        $this->boundSql = str_replace('{t}', Soul_Config::$dbTablePrefix, $this->originSql);
        if (is_array($params)) {
            preg_match_all('/(:[a-zA-Z]\w+)(:[isbad])?/', $this->boundSql, $matches);
            $paramNames = $matches[1];
            $paramTypes = $matches[2];
            for ($i = 0; $i < count($paramNames); $i++) {
                $n = substr($paramNames[$i], 1);
                $t = empty($paramTypes[$i]) ? 's' : substr($paramTypes[$i], 1);
                $v = $params[$n];
                if(is_null($v)){
                	$v = 'NULL';
                }else{
                	$escapeValue = $this->escapeString($v);
	                switch($t){
	                	case 'i':
                        case 'f':
	                		$v = $escapeValue;
	                		break;
	                	case 'a':
	                		$nv = array();
	                		foreach ($escapeValue as $sv) {
								$nv[] = "'".$sv."'";
	                    	}
	                    	$v = implode(',', $nv);
	                    	break;                  	
	                	default:
	                		$v = "'".$escapeValue."'";
	                		break;
	                }
                }
                $this->boundSql = str_replace($matches[0][$i], $v, $this->boundSql);
                
            }
        }
        return $this;
    }

    public function connect() {
        if (!$this->_link) {
            $this->_link = new Mysqli($this->getServer(), $this->getUsername(), $this->getPassword(), $this->getDatabase());
            if ($this->_link->connect_errno) {
                throw new Soul_Db_Exception($this->_link->connect_errno . ';' . $this->_link->connect_error);
            }
        }
        return $this;
    }

    public function disconnect() {
        if ($this->_link) {
            $this->_link->close();
            $this->_link = NULL;
        }
    }

    public function getInsertId() {
        return $this->_link->insert_id;
    }

    public function beginTransaction() {
        if ($this->_isTransaction === FALSE) {
            $this->_link->autocommit(FALSE);
            $this->_isTransaction = TRUE;
        }
        return $this;
    }

    public function commitTransaction() {
        if ($this->_isTransaction) {
            $this->_link->commit();
            $this->_isTransaction = FALSE;
        }
        return $this;
    }

    public function rollbackTransaction() {
        if ($this->_isTransaction) {
            $this->_link->rollback();
            $this->_isTransaction = FALSE;
        }
        return $this;
    }

    public function execute() {
        $this->_link->query("SET NAMES '".Soul_Config::$dbEncoding."'");
        return $this->_link->query($this->boundSql);
    }
    
	public function executeBatch($sql, $split = ';') {
        $arr = explode($split, $sql);
        $this->_link->query("SET NAMES '".Soul_Config::$dbEncoding."'");
        try{
            $this->beginTransaction();
            foreach($arr as $str){
                if(!empty($str)){
                    $this->_link->query($str);
                }
            }
            $this->commitTransaction();
        }catch(Exception $ex){
            $this->rollbackTransaction();
            throw $ex;
        }
        return $this;
	}    

    public function getAffectedRows() {
        return $this->_link->affected_rows;
    }

    public function fetch() {
        $iter = $this->execute();
        $result = new Soul_Db_Result();
        while ($row = $iter->fetch_assoc()) {
        	if(!Soul_Util_Enviroment::getMagicQuotesRuntime()){
        		foreach($row as $name=>&$value){
        			$value = stripslashes($value);
        		}
        	}
            $result->append($row);
        }
        $iter->close();
        return $result;
    }

    public function fetchLimit($limit, $offset) {
        if (!empty($limit) && $limit > 0) {
            $this->boundSql .= "\nLIMIT $offset,$limit";
        }
        return $this->fetch();
    }

}